1. Technical Field
This disclosure generally relates to debuggers, and more specifically relates to breakpoints in debuggers.
2. Background Art
Computer systems have evolved into extremely sophisticated devices, and may be found in many different settings. Computer systems typically include a combination of hardware, such as semiconductors and circuit boards, and software, also known as computer programs. As advances in semiconductor processing and computer architecture push the performance of the computer hardware higher, more sophisticated computer software has evolved to take advantage of the higher performance of the hardware, resulting in computer systems today that are much more powerful than just a few years ago.
As the sophistication and complexity of computer software increase, the more difficult the software is to debug. Debugging is the process of finding problems, or “bugs”, during the development of a computer program. Most modern programming environments include a debugger that provides tools for testing and debugging a computer program. Debuggers allow setting breakpoints in a computer program. When a breakpoint is encountered, execution of the computer program is halted to allow inspecting the state of the computer program, or to allow executing instructions one at a time in a single step mode.
Breakpoints are typically implemented in a debugger using a breakpoint table that is maintained in the debugger. When a user sets a breakpoint, the breakpoint is written to the breakpoint table. The original instruction at the location in the code where the breakpoint is set is written to the breakpoint table. The instruction in the code is then replaced with a software trap instruction that turns control over to the debugger when executed. When the breakpoint is no longer desired, the software trap instruction in the code is replaced with the original instruction stored in the breakpoint table, thereby restoring the code to its original state, and the breakpoint is then removed from the breakpoint table.
A problem may arise when debugging self-modifying code. One example of self-modifying code is code that is generated by a Just-In-Time (JIT) compiler. Using traditional breakpoints in self-modifying code can lead to unexpected and undesirable behavior. For example, let's assume a user sets a breakpoint in the code, which causes the original instruction to be stored in the breakpoint table, and causes a trap instruction to be inserted in the place of the original instruction. If a JIT compiler then overwrites the trap instruction, undesirable things will happen. First, the debugger thinks a breakpoint is set at that instruction because its breakpoint table indicates the breakpoint is set, but execution of the code will not halt because the trap instruction was overwritten by a different instruction. Second, because the debugger thinks the breakpoint is still set, when the user removes the breakpoint, the instruction that was written by the JIT compiler will be overwritten with the old instruction that was originally in that location. This will cause the code to execute incorrectly, and leads to unpredictable and undesirable results. Without a way to protect breakpoints from being affected by self-modifying code, known debuggers will continue to suffer from the problems described above.